package com.qqs.distributed.advice;

import org.springframework.aop.support.AopUtils;
import org.springframework.core.MethodClassKey;
import org.springframework.util.ClassUtils;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public abstract class AbstractCacheAttributeSource implements CacheAttributeSource {

    private final Map<Object, CacheAttribute> attributeCache = new ConcurrentHashMap<>(1024);

    @Override
    public CacheAttribute getCacheAttribute(Method method, Class<?> targetClass) {
        if (method.getDeclaringClass() == Object.class) {
            return null;
        }
        Object cacheKey = getCacheKey(method, targetClass);
        CacheAttribute cacheAttribute = attributeCache.get(cacheKey);
        // 先判断缓存中是否存在,存在直接返回
        if (cacheAttribute != null) {
            if (cacheAttribute == NULL_CACHE_ATTRIBUTE) {
                return null;
            } else {
                return cacheAttribute;
            }
        }
        cacheAttribute = computeCacheAttribute(method, targetClass);
        if (cacheAttribute == null) {
            attributeCache.put(cacheKey, CacheAttributeSource.NULL_CACHE_ATTRIBUTE);
        } else {
            attributeCache.put(cacheKey, cacheAttribute);
        }
        return cacheAttribute;
    }

    protected Object getCacheKey(Method method, Class<?> targetClass) {
        return new MethodClassKey(method, targetClass);
    }

    protected CacheAttribute computeCacheAttribute(Method method, Class<?> targetClass) {
        // 判断方法是否是公开的,如果不是则直接返回null
        if (!Modifier.isPublic(method.getModifiers())) {
            return null;
        }
        // The method may be on an interface, but we need attributes from the target class.
        // If the target class is null, the method will be unchanged.
        Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
        // 先尝试在方法上找
        CacheAttribute attribute = doFindCacheAttribute(specificMethod);
        if (attribute != null) {
            return attribute;
        }

        // 如果类上没有找到,尝试让方法回退到原始的方法再查询找
        if (specificMethod != method) {
            return doFindCacheAttribute(method);
        }
        return null;
    }

    protected CacheAttribute doFindCacheAttribute(Method method) {
        CacheAttribute attribute = findCacheAttribute(method);
        if (attribute != null) {
            attribute.setReturnType(method.getGenericReturnType());
            attribute.setMethod(method);
            return attribute;
        }
        // 如果方法上没有找到,则从类上找
        attribute = findCacheAttribute(method.getDeclaringClass());
        if (attribute != null && ClassUtils.isUserLevelMethod(method)) {
            attribute.setReturnType(method.getGenericReturnType());
            attribute.setMethod(method);
            return attribute;
        }
        return null;
    }

    abstract CacheAttribute findCacheAttribute(Method method);

    abstract CacheAttribute findCacheAttribute(Class<?> targetClass);

}
